Data Preparation
Read In
input_dir <- fs::path("../input")
files <- fs::dir_ls(input_dir, glob = "*.csv")
his_dt <- read_csv(
files[1],
col_types = cols(
package = col_character(),
version = col_character(),
date = col_date(format = "%Y-%m-%d"),
repository = col_character()
)
)
ov_dt <- read_csv(
files[2],
col_types = cols(
package = col_character(),
version = col_character(),
depends = col_character(),
imports = col_character(),
license = col_character(),
needs_compilation = col_logical(),
author = col_character(),
bug_reports = col_character(),
url = col_character(),
date_published = col_date(format = "%Y-%m-%d"),
description = col_character(),
title = col_character()
)
)
Quick View
dplyr::glimpse(ov_dt, 100)
Rows: 18,388
Columns: 12
$ package <chr> "A3", "AATtools", "ABACUS", "abbreviate", "abbyyR", "abc", "abc.data", "…
$ version <chr> "1.0.0", "0.0.1", "1.0.0", "0.1", "0.5.5", "2.2.1", "1.0", "0.9.0", "1.0…
$ depends <chr> "R (>= 2.15.0), xtable, pbapply", "R (>= 3.6.0)", "R (>= 3.1.0)", NA, "R…
$ imports <chr> NA, "magrittr, dplyr, doParallel, foreach", "ggplot2 (>= 3.1.0), shiny (…
$ license <chr> "GPL (>= 2)", "GPL-3", "GPL-3", "GPL-3", "MIT + file LICENSE", "GPL (>= …
$ needs_compilation <lgl> FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, TRUE, FALSE, TRU…
$ author <chr> "Scott Fortmann-Roe", "Sercan Kahveci [aut, cre]", "Mintu Nath [aut, cre…
$ bug_reports <chr> NA, "https://github.com/Spiritspeak/AATtools/issues", NA, NA, "http://gi…
$ url <chr> NA, NA, "https://shiny.abdn.ac.uk/Stats/apps/", "https://github.com/sigb…
$ date_published <date> 2015-08-16, 2020-06-14, 2019-09-20, 2021-12-14, 2019-06-25, 2022-05-19,…
$ description <chr> "Supplies tools for tabulating and analyzing the results of predictive m…
$ title <chr> "Accurate, Adaptable, and Accessible Error Metrics for Predictive\nModel…
Questions
- How long did packages take from their first release to version
1.0?
- What type of packages were most frequent in different years?
- Who are the most productive authors?
- Can you predict the growth toward 2025?
- What license is most used? Has there been a change over time?
- How many packages use all CAPS, all small, or a mixture?
- How have the dependencies & imports changed over time?
- Which repositories do packages use? Github/Bitbucket etc. How do
these vary over time?
- Do packages have URLs for bug reports?
- Is there any temporal patterns to when versions are submitted to
CRAN?
- Have titles & descriptions gotten longer over time?
- Do authors use minor versions?
Features
Separate version
ov_dt <- ov_dt |>
separate(
version,
into =
c("major", "minor", "patch"),
sep = "\\.",
extra = "merge",
fill = "right",
remove = FALSE
)
Number of dependencies
ov_dt <- ov_dt |>
mutate(
num_dep = purrr::map_int(
.x = depends,
.f = function(x){
x |>
stringr::str_split(",", simplify = TRUE) |>
length()
}
),
num_dep = ifelse(is.na(depends), 0, num_dep)
)
Number of imports
ov_dt <- ov_dt |>
mutate(
num_imports = purrr::map_int(
.x = imports,
.f = function(x){
x |>
stringr::str_split(",", simplify = TRUE) |>
length()
}
),
num_imports = ifelse(is.na(imports), 0, num_imports)
)
Number of authors
ov_dt <- ov_dt |>
mutate(
num_authors = purrr::map_int(
.x = author,
.f = function(x){
x |>
stringr::str_split(",", simplify = TRUE) |>
length()
}
)
)
Temporal features
ov_dt <- ov_dt |>
mutate(
year = lubridate::year(date_published),
month = lubridate::month(date_published, label = TRUE),
day = lubridate::day(date_published),
wday = lubridate::wday(date_published, label = TRUE),
yr_mon = sprintf("%d-%s", year, month),
dt = lubridate::ym(paste0(year, "-", month))
)
Warning: 1 failed to parse.
Title & Description Lengths
ov_dt <- ov_dt |>
mutate(
len_title = purrr::map_int(title, ~ stringr::str_count(.x, "\\w+")),
len_desc = purrr::map_int(description, ~ stringr::str_count(.x, "\\w+"))
)
License
ov_dt <- ov_dt |>
mutate(
license_cleaned = case_when(
stringr::str_detect(license, "^GPL-3") ~ "GPL-3",
stringr::str_detect(license, "^GPL\\s\\([\\s\\d\\.<=>]*3") ~ "GPL-3",
stringr::str_detect(license, "^GPL-2") ~ "GPL-2",
stringr::str_detect(license, "^GPL\\s\\([\\s\\d\\.<=>]*2") ~ "GPL-2",
stringr::str_detect(license, "^AGPL") ~ "AGPL",
stringr::str_detect(license, "^LGPL") ~ "LGPL",
stringr::str_detect(license, "Apache") ~ "Apache",
stringr::str_detect(license, "BSD") ~ "BSD",
stringr::str_detect(license, "LGPL") ~ "LGPL",
# stringr::str_detect(license, "GNU") ~ "GNU",
stringr::str_detect(license, "MIT") ~ "MIT",
stringr::str_detect(license, "CC0") ~ "CC0",
# stringr::str_detect(license, "MPL") ~ "MPL",
# stringr::str_detect(license, "Unlimited") ~ "Unlimited",
# stringr::str_detect(license, "^CC") ~ "CC",
license == "GPL" ~ "GPL",
TRUE ~ "Other"
)
)
Bug Report Domain
Temporal Questions
How long did packages take from their first release to version
1.0?
What type of packages were most frequent in different
years?
Who are the most productive authors?
Can you predict the growth toward 2025?
What license is most used? Has there been a change over time? -
done
How many packages use all CAPS, all small, or a mixture?
How have the dependencies & imports changed over
time?
Which repositories do packages use? Github/Bitbucket etc. How do
these vary over time?
Do packages have URLs for bug reports?
Is there any temporal patterns to when versions are submitted to
CRAN?
Do authors use minor versions?
Have titles & descriptions gotten longer over time? -
done
How have the dependencies & imports changed over
time?
ov_dt |>
group_by(dt) |>
summarise_at(vars(num_dep, num_imports), list(mean = mean)) |>
ggplot(aes(x= dt)) +
geom_jitter(aes(y = num_dep_mean, color = "num_dep_mean"), alpha = 0.2) +
geom_smooth(aes(y = num_dep_mean, color = "num_dep_mean"), span = 0.3, se = FALSE) +
geom_jitter(aes(y = num_imports_mean, color = "num_imports_mean"), alpha = 0.2) +
geom_smooth(aes(y = num_imports_mean, color = "num_imports_mean"), span = 0.3, se = FALSE) +
theme_light()

- Have titles & descriptions gotten longer over time?
ov_dt |>
group_by(dt) |>
summarise_at(vars(len_title, len_desc), list(median = median, sd = sd), na.rm = TRUE) |>
ggplot(aes(x= dt)) +
geom_jitter(aes(y = len_title_median, color = "len_title_median"), alpha = 0.2) +
geom_smooth(aes(y = len_title_median, color = "len_title_median"), span = 0.3, se = FALSE) +
geom_jitter(aes(y = len_desc_median, color = "len_desc_median"), alpha = 0.2) +
geom_smooth(aes(y = len_desc_median, color = "len_desc_median"), span = 0.3, se = FALSE) +
theme_light()


ov_dt |>
ggplot(aes(x= date_published, y = len_title)) +
geom_jitter(alpha = 0.05) +
geom_smooth(span = 0.1, se = FALSE) +
theme_light() +
scale_y_log10()

ov_dt |>
ggplot(aes(x= date_published, y = len_desc)) +
geom_jitter(alpha = 0.05) +
geom_smooth(span = 0.2, se = FALSE) +
theme_light() +
scale_y_log10()

- What license is most used? Has there been a change over time?
ov_dt |>
group_by(license_cleaned) |>
count() |>
ggplot(aes(x = forcats::fct_reorder(license_cleaned, n), y = n, fill = license_cleaned)) +
geom_col() +
coord_flip() +
theme_minimal() +
guides(fill = FALSE) +
labs(x = "", y = "")
Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> = "none")` instead.

ov_dt |>
group_by(dt) |>
count(license_cleaned) |>
mutate(license_cleaned = forcats::fct_reorder(license_cleaned, n)) |>
ggplot(aes(x= dt, y = n, color = license_cleaned)) +
# geom_line( alpha = 0.3) +
geom_jitter(alpha = 0.3) +
geom_smooth(span = 0.3, se = FALSE) +
theme_light()

ov_dt |>
group_by(dt) |>
count(license_cleaned) |>
mutate(license_cleaned = forcats::fct_reorder(license_cleaned, n)) |>
ggplot(aes(x= dt, y = n, color = license_cleaned)) +
# geom_line( alpha = 0.3) +
geom_jitter(alpha = 0.3) +
geom_smooth(span = 0.8, se = FALSE) +
theme_light() +
scale_y_log10()

- Do packages have URLs for bug reports?
ov_dt |>
group_by(dt) |>
count(url_exist = is.na(url)) |>
ggplot(aes(x= dt, y = n, color = url_exist)) +
geom_jitter(alpha = 0.3) +
geom_smooth(span = 0.3, se = FALSE) +
theme_light()

ov_dt |>
group_by(dt) |>
count(url_exist = is.na(bug_reports)) |>
ggplot(aes(x= dt, y = n, color = url_exist)) +
geom_jitter(alpha = 0.3) +
geom_smooth(span = 0.3, se = FALSE) +
theme_light()

- Which repositories do packages use? Github/Bitbucket etc. How do
these vary over time?

- Is there any temporal patterns to when versions are submitted to
CRAN?
ov_dt |>
group_by(dt) |>
count() |>
ggplot(aes(dt, n)) +
geom_line()
ov_dt |> filter(!is.na(dt)) |> count(dt) |> arrange(dt) |> timetk::pad_by_time(.by = "month", .pad_value = 0) -> xdat
.date_var is missing. Using: dt
timetk::plot_seasonal_diagnostics(xdat, dt, n)
LS0tCnRpdGxlOiAiQ1JBTiBIaXN0b3J5IEVEQSIKYXV0aG9yOiAiUiBTYW5nb2xlIgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKLS0tCgpgYGB7ciBsaWJyYXJpZXMsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CmxpYnJhcnkoZHBseXIpCmxpYnJhcnkodGlkeXIpCmxpYnJhcnkocmVhZHIpCmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeShsYXR0aWNlKQpsaWJyYXJ5KG5hbmlhcikKbGlicmFyeShza2ltcikKYGBgCgojIEludHJvZHVjdGlvbgoKCgojIERhdGEgUHJlcGFyYXRpb24gey50YWJzZXR9CgojIyBSZWFkIEluIHsudGFic2V0fQoKYGBge3J9CmlucHV0X2RpciA8LSBmczo6cGF0aCgiLi4vaW5wdXQiKQpmaWxlcyA8LSBmczo6ZGlyX2xzKGlucHV0X2RpciwgZ2xvYiA9ICIqLmNzdiIpCmhpc19kdCA8LSByZWFkX2NzdigKICBmaWxlc1sxXSwKICBjb2xfdHlwZXMgPSBjb2xzKAogICAgcGFja2FnZSA9IGNvbF9jaGFyYWN0ZXIoKSwKICAgIHZlcnNpb24gPSBjb2xfY2hhcmFjdGVyKCksCiAgICBkYXRlID0gY29sX2RhdGUoZm9ybWF0ID0gIiVZLSVtLSVkIiksCiAgICByZXBvc2l0b3J5ID0gY29sX2NoYXJhY3RlcigpCiAgKQopCm92X2R0IDwtIHJlYWRfY3N2KAogIGZpbGVzWzJdLAogIGNvbF90eXBlcyA9IGNvbHMoCiAgICBwYWNrYWdlID0gY29sX2NoYXJhY3RlcigpLAogICAgdmVyc2lvbiA9IGNvbF9jaGFyYWN0ZXIoKSwKICAgIGRlcGVuZHMgPSBjb2xfY2hhcmFjdGVyKCksCiAgICBpbXBvcnRzID0gY29sX2NoYXJhY3RlcigpLAogICAgbGljZW5zZSA9IGNvbF9jaGFyYWN0ZXIoKSwKICAgIG5lZWRzX2NvbXBpbGF0aW9uID0gY29sX2xvZ2ljYWwoKSwKICAgIGF1dGhvciA9IGNvbF9jaGFyYWN0ZXIoKSwKICAgIGJ1Z19yZXBvcnRzID0gY29sX2NoYXJhY3RlcigpLAogICAgdXJsID0gY29sX2NoYXJhY3RlcigpLAogICAgZGF0ZV9wdWJsaXNoZWQgPSBjb2xfZGF0ZShmb3JtYXQgPSAiJVktJW0tJWQiKSwKICAgIGRlc2NyaXB0aW9uID0gY29sX2NoYXJhY3RlcigpLAogICAgdGl0bGUgPSBjb2xfY2hhcmFjdGVyKCkKICApCikKYGBgCgojIyBRdWljayBWaWV3IHsudGFic2V0fQoKYGBge3J9CmRwbHlyOjpnbGltcHNlKG92X2R0LCAxMDApCmBgYAoKCgojIERhdGEgUXVhbGl0eQoKYGBge3J9Cm92X2R0IHw+IAogIGRwbHlyOjphcnJhbmdlKGRhdGVfcHVibGlzaGVkKSB8PiAKICB2aXNfbWlzcygpCmBgYAoKIyBRdWVzdGlvbnMKCiogSG93IGxvbmcgZGlkIHBhY2thZ2VzIHRha2UgZnJvbSB0aGVpciBmaXJzdCByZWxlYXNlIHRvIHZlcnNpb24gMS4wPyAKKiBXaGF0IHR5cGUgb2YgcGFja2FnZXMgd2VyZSBtb3N0IGZyZXF1ZW50IGluIGRpZmZlcmVudCB5ZWFycz8KKiBXaG8gYXJlIHRoZSBtb3N0IHByb2R1Y3RpdmUgYXV0aG9ycz8gCiogQ2FuIHlvdSBwcmVkaWN0IHRoZSBncm93dGggdG93YXJkIDIwMjU/CiogV2hhdCBsaWNlbnNlIGlzIG1vc3QgdXNlZD8gSGFzIHRoZXJlIGJlZW4gYSBjaGFuZ2Ugb3ZlciB0aW1lPwoqIEhvdyBtYW55IHBhY2thZ2VzIHVzZSBhbGwgQ0FQUywgYWxsIHNtYWxsLCBvciBhIG1peHR1cmU/CiogSG93IGhhdmUgdGhlIGRlcGVuZGVuY2llcyAmIGltcG9ydHMgY2hhbmdlZCBvdmVyIHRpbWU/CiogV2hpY2ggcmVwb3NpdG9yaWVzIGRvIHBhY2thZ2VzIHVzZT8gR2l0aHViL0JpdGJ1Y2tldCBldGMuIEhvdyBkbyB0aGVzZSB2YXJ5IG92ZXIgdGltZT8KKiBEbyBwYWNrYWdlcyBoYXZlIFVSTHMgZm9yIGJ1ZyByZXBvcnRzPwoqIElzIHRoZXJlIGFueSB0ZW1wb3JhbCBwYXR0ZXJucyB0byB3aGVuIHZlcnNpb25zIGFyZSBzdWJtaXR0ZWQgdG8gQ1JBTj8KKiBIYXZlIHRpdGxlcyAmIGRlc2NyaXB0aW9ucyBnb3R0ZW4gbG9uZ2VyIG92ZXIgdGltZT8KKiBEbyBhdXRob3JzIHVzZSBtaW5vciB2ZXJzaW9ucz8KCiMjIEZlYXR1cmVzCgpTZXBhcmF0ZSB2ZXJzaW9uCgpgYGB7cn0Kb3ZfZHQgPC0gb3ZfZHQgfD4KICBzZXBhcmF0ZSgKICAgIHZlcnNpb24sCiAgICBpbnRvID0KICAgICAgYygibWFqb3IiLCAibWlub3IiLCAicGF0Y2giKSwKICAgIHNlcCA9ICJcXC4iLAogICAgZXh0cmEgPSAibWVyZ2UiLAogICAgZmlsbCA9ICJyaWdodCIsCiAgICByZW1vdmUgPSBGQUxTRQogICkKYGBgCgoKTnVtYmVyIG9mIGRlcGVuZGVuY2llcwoKYGBge3J9Cm92X2R0IDwtIG92X2R0IHw+IAogIG11dGF0ZSgKICAgIG51bV9kZXAgPSBwdXJycjo6bWFwX2ludCgKICAgICAgLnggPSBkZXBlbmRzLAogICAgICAuZiA9IGZ1bmN0aW9uKHgpewogICAgICAgIHggfD4gCiAgICAgICAgICBzdHJpbmdyOjpzdHJfc3BsaXQoIiwiLCBzaW1wbGlmeSA9IFRSVUUpIHw+IAogICAgICAgICAgbGVuZ3RoKCkKICAgICAgfQogICAgKSwKICAgIG51bV9kZXAgPSBpZmVsc2UoaXMubmEoZGVwZW5kcyksIDAsIG51bV9kZXApCiAgKQpgYGAKCk51bWJlciBvZiBpbXBvcnRzCgpgYGB7cn0Kb3ZfZHQgPC0gb3ZfZHQgfD4gCiAgbXV0YXRlKAogICAgbnVtX2ltcG9ydHMgPSBwdXJycjo6bWFwX2ludCgKICAgICAgLnggPSBpbXBvcnRzLAogICAgICAuZiA9IGZ1bmN0aW9uKHgpewogICAgICAgIHggfD4gCiAgICAgICAgICBzdHJpbmdyOjpzdHJfc3BsaXQoIiwiLCBzaW1wbGlmeSA9IFRSVUUpIHw+IAogICAgICAgICAgbGVuZ3RoKCkKICAgICAgfQogICAgKSwKICAgIG51bV9pbXBvcnRzID0gaWZlbHNlKGlzLm5hKGltcG9ydHMpLCAwLCBudW1faW1wb3J0cykKICApCmBgYAoKTnVtYmVyIG9mIGF1dGhvcnMKCmBgYHtyfQpvdl9kdCA8LSBvdl9kdCB8PiAKICBtdXRhdGUoCiAgICBudW1fYXV0aG9ycyA9IHB1cnJyOjptYXBfaW50KAogICAgICAueCA9IGF1dGhvciwKICAgICAgLmYgPSBmdW5jdGlvbih4KXsKICAgICAgICB4IHw+IAogICAgICAgICAgc3RyaW5ncjo6c3RyX3NwbGl0KCIsIiwgc2ltcGxpZnkgPSBUUlVFKSB8PiAKICAgICAgICAgIGxlbmd0aCgpCiAgICAgIH0KICAgICkKICApCmBgYAoKVGVtcG9yYWwgZmVhdHVyZXMKYGBge3J9Cm92X2R0IDwtIG92X2R0IHw+IAogIG11dGF0ZSgKICAgIHllYXIgPSBsdWJyaWRhdGU6OnllYXIoZGF0ZV9wdWJsaXNoZWQpLAogICAgbW9udGggPSBsdWJyaWRhdGU6Om1vbnRoKGRhdGVfcHVibGlzaGVkLCBsYWJlbCA9IFRSVUUpLAogICAgZGF5ID0gbHVicmlkYXRlOjpkYXkoZGF0ZV9wdWJsaXNoZWQpLAogICAgd2RheSA9IGx1YnJpZGF0ZTo6d2RheShkYXRlX3B1Ymxpc2hlZCwgbGFiZWwgPSBUUlVFKSwKICAgIHlyX21vbiA9IHNwcmludGYoIiVkLSVzIiwgeWVhciwgbW9udGgpLAogICAgZHQgPSBsdWJyaWRhdGU6OnltKHBhc3RlMCh5ZWFyLCAiLSIsIG1vbnRoKSkKICApCmBgYAoKClRpdGxlICYgRGVzY3JpcHRpb24gTGVuZ3RocwoKYGBge3J9Cm92X2R0IDwtIG92X2R0IHw+CiAgbXV0YXRlKAogICAgbGVuX3RpdGxlID0gcHVycnI6Om1hcF9pbnQodGl0bGUsIH4gc3RyaW5ncjo6c3RyX2NvdW50KC54LCAiXFx3KyIpKSwKICAgIGxlbl9kZXNjID0gcHVycnI6Om1hcF9pbnQoZGVzY3JpcHRpb24sIH4gc3RyaW5ncjo6c3RyX2NvdW50KC54LCAiXFx3KyIpKQogICkKYGBgCgoKTGljZW5zZSAKCmBgYHtyfQpvdl9kdCA8LSBvdl9kdCB8PiAKICBtdXRhdGUoCiAgICBsaWNlbnNlX2NsZWFuZWQgPSBjYXNlX3doZW4oCiAgICAgIHN0cmluZ3I6OnN0cl9kZXRlY3QobGljZW5zZSwgIl5HUEwtMyIpIH4gIkdQTC0zIiwKICAgICAgc3RyaW5ncjo6c3RyX2RldGVjdChsaWNlbnNlLCAiXkdQTFxcc1xcKFtcXHNcXGRcXC48PT5dKjMiKSB+ICJHUEwtMyIsCiAgICAgIHN0cmluZ3I6OnN0cl9kZXRlY3QobGljZW5zZSwgIl5HUEwtMiIpIH4gIkdQTC0yIiwKICAgICAgc3RyaW5ncjo6c3RyX2RldGVjdChsaWNlbnNlLCAiXkdQTFxcc1xcKFtcXHNcXGRcXC48PT5dKjIiKSB+ICJHUEwtMiIsCiAgICAgIHN0cmluZ3I6OnN0cl9kZXRlY3QobGljZW5zZSwgIl5BR1BMIikgfiAiQUdQTCIsCiAgICAgIHN0cmluZ3I6OnN0cl9kZXRlY3QobGljZW5zZSwgIl5MR1BMIikgfiAiTEdQTCIsCiAgICAgIHN0cmluZ3I6OnN0cl9kZXRlY3QobGljZW5zZSwgIkFwYWNoZSIpIH4gIkFwYWNoZSIsCiAgICAgIHN0cmluZ3I6OnN0cl9kZXRlY3QobGljZW5zZSwgIkJTRCIpIH4gIkJTRCIsCiAgICAgIHN0cmluZ3I6OnN0cl9kZXRlY3QobGljZW5zZSwgIkxHUEwiKSB+ICJMR1BMIiwKICAgICAgIyBzdHJpbmdyOjpzdHJfZGV0ZWN0KGxpY2Vuc2UsICJHTlUiKSB+ICJHTlUiLAogICAgICBzdHJpbmdyOjpzdHJfZGV0ZWN0KGxpY2Vuc2UsICJNSVQiKSB+ICJNSVQiLAogICAgICBzdHJpbmdyOjpzdHJfZGV0ZWN0KGxpY2Vuc2UsICJDQzAiKSB+ICJDQzAiLAogICAgICAjIHN0cmluZ3I6OnN0cl9kZXRlY3QobGljZW5zZSwgIk1QTCIpIH4gIk1QTCIsCiAgICAgICMgc3RyaW5ncjo6c3RyX2RldGVjdChsaWNlbnNlLCAiVW5saW1pdGVkIikgfiAiVW5saW1pdGVkIiwKICAgICAgIyBzdHJpbmdyOjpzdHJfZGV0ZWN0KGxpY2Vuc2UsICJeQ0MiKSB+ICJDQyIsCiAgICAgIGxpY2Vuc2UgPT0gIkdQTCIgfiAiR1BMIiwKICAgICAgVFJVRSB+ICJPdGhlciIKICAgICAgKQogICkKYGBgCgpCdWcgUmVwb3J0IERvbWFpbgoKYGBge3J9Cm92X2R0IDwtIG92X2R0IHw+CiAgbXV0YXRlKGRvbWFpbiA9IHB1cnJyOjptYXBfY2hyKGJ1Z19yZXBvcnRzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB+IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZiAoaXMubmEoLngpKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmV0dXJuKCIiKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGVsc2UKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJldHVybih1cmx0b29sczo6dXJsX3BhcnNlKC54KSRkb21haW4pCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0pKQpgYGAKCgojIFRlbXBvcmFsIFF1ZXN0aW9ucwoKKiBIb3cgbG9uZyBkaWQgcGFja2FnZXMgdGFrZSBmcm9tIHRoZWlyIGZpcnN0IHJlbGVhc2UgdG8gdmVyc2lvbiAxLjA/IAoqIFdoYXQgdHlwZSBvZiBwYWNrYWdlcyB3ZXJlIG1vc3QgZnJlcXVlbnQgaW4gZGlmZmVyZW50IHllYXJzPwoqIFdobyBhcmUgdGhlIG1vc3QgcHJvZHVjdGl2ZSBhdXRob3JzPyAKKiBDYW4geW91IHByZWRpY3QgdGhlIGdyb3d0aCB0b3dhcmQgMjAyNT8KKiBXaGF0IGxpY2Vuc2UgaXMgbW9zdCB1c2VkPyBIYXMgdGhlcmUgYmVlbiBhIGNoYW5nZSBvdmVyIHRpbWU/IC0gZG9uZQoqIEhvdyBtYW55IHBhY2thZ2VzIHVzZSBhbGwgQ0FQUywgYWxsIHNtYWxsLCBvciBhIG1peHR1cmU/CiogSG93IGhhdmUgdGhlIGRlcGVuZGVuY2llcyAmIGltcG9ydHMgY2hhbmdlZCBvdmVyIHRpbWU/CiogV2hpY2ggcmVwb3NpdG9yaWVzIGRvIHBhY2thZ2VzIHVzZT8gR2l0aHViL0JpdGJ1Y2tldCBldGMuIEhvdyBkbyB0aGVzZSB2YXJ5IG92ZXIgdGltZT8KKiBEbyBwYWNrYWdlcyBoYXZlIFVSTHMgZm9yIGJ1ZyByZXBvcnRzPwoqIElzIHRoZXJlIGFueSB0ZW1wb3JhbCBwYXR0ZXJucyB0byB3aGVuIHZlcnNpb25zIGFyZSBzdWJtaXR0ZWQgdG8gQ1JBTj8KKiBEbyBhdXRob3JzIHVzZSBtaW5vciB2ZXJzaW9ucz8KKiBIYXZlIHRpdGxlcyAmIGRlc2NyaXB0aW9ucyBnb3R0ZW4gbG9uZ2VyIG92ZXIgdGltZT8gLSBkb25lCgoKKiBIb3cgaGF2ZSB0aGUgZGVwZW5kZW5jaWVzICYgaW1wb3J0cyBjaGFuZ2VkIG92ZXIgdGltZT8KCmBgYHtyfQpvdl9kdCB8PiAKICAgIGdyb3VwX2J5KGR0KSB8PiAKICAgIHN1bW1hcmlzZV9hdCh2YXJzKG51bV9kZXAsIG51bV9pbXBvcnRzKSwgbGlzdChtZWFuID0gbWVhbikpIHw+IAogIGdncGxvdChhZXMoeD0gZHQpKSArCiAgICBnZW9tX2ppdHRlcihhZXMoeSA9IG51bV9kZXBfbWVhbiwgY29sb3IgPSAibnVtX2RlcF9tZWFuIiksIGFscGhhID0gMC4yKSArCiAgZ2VvbV9zbW9vdGgoYWVzKHkgPSBudW1fZGVwX21lYW4sIGNvbG9yID0gIm51bV9kZXBfbWVhbiIpLCBzcGFuID0gMC4zLCBzZSA9IEZBTFNFKSArCiAgZ2VvbV9qaXR0ZXIoYWVzKHkgPSBudW1faW1wb3J0c19tZWFuLCBjb2xvciA9ICJudW1faW1wb3J0c19tZWFuIiksIGFscGhhID0gMC4yKSArCiAgZ2VvbV9zbW9vdGgoYWVzKHkgPSBudW1faW1wb3J0c19tZWFuLCBjb2xvciA9ICJudW1faW1wb3J0c19tZWFuIiksIHNwYW4gPSAwLjMsIHNlID0gRkFMU0UpICsKICB0aGVtZV9saWdodCgpCmBgYAoKKiBIYXZlIHRpdGxlcyAmIGRlc2NyaXB0aW9ucyBnb3R0ZW4gbG9uZ2VyIG92ZXIgdGltZT8gCgpgYGB7cn0Kb3ZfZHQgfD4gCiAgICBncm91cF9ieShkdCkgfD4gCiAgICBzdW1tYXJpc2VfYXQodmFycyhsZW5fdGl0bGUsIGxlbl9kZXNjKSwgbGlzdChtZWRpYW4gPSBtZWRpYW4sIHNkID0gc2QpLCBuYS5ybSA9IFRSVUUpIHw+IAogIGdncGxvdChhZXMoeD0gZHQpKSArCiAgICBnZW9tX2ppdHRlcihhZXMoeSA9IGxlbl90aXRsZV9tZWRpYW4sIGNvbG9yID0gImxlbl90aXRsZV9tZWRpYW4iKSwgYWxwaGEgPSAwLjIpICsKICBnZW9tX3Ntb290aChhZXMoeSA9IGxlbl90aXRsZV9tZWRpYW4sIGNvbG9yID0gImxlbl90aXRsZV9tZWRpYW4iKSwgc3BhbiA9IDAuMywgc2UgPSBGQUxTRSkgKwogIGdlb21faml0dGVyKGFlcyh5ID0gbGVuX2Rlc2NfbWVkaWFuLCBjb2xvciA9ICJsZW5fZGVzY19tZWRpYW4iKSwgYWxwaGEgPSAwLjIpICsKICBnZW9tX3Ntb290aChhZXMoeSA9IGxlbl9kZXNjX21lZGlhbiwgY29sb3IgPSAibGVuX2Rlc2NfbWVkaWFuIiksIHNwYW4gPSAwLjMsIHNlID0gRkFMU0UpICsKICB0aGVtZV9saWdodCgpCmBgYAoKYGBge3J9Cm92X2R0IHw+IAogIGZpbHRlcih5ZWFyICVpbiUgYygyMDIyLCAyMDIwLCAyMDE4KSkgfD4gCiAgZ2dwbG90KCkgKwogIGdlb21fZGVuc2l0eShhZXMoeCA9IGxlbl9kZXNjLCAKICAgICAgICAgICAgICAgICAgIGZpbGwgPSBhcy5mYWN0b3IoeWVhciksIAogICAgICAgICAgICAgICAgICAgY29sb3IgPSBhcy5mYWN0b3IoeWVhcikKICAgICAgICAgICAgICAgICAgICksCiAgICAgICAgICAgICAgIGFscGhhID0gMC4zCiAgICAgICAgICAgICAgICkKb3ZfZHQgfD4gCiAgZmlsdGVyKHllYXIgJWluJSBjKDIwMjIsIDIwMjAsIDIwMTgpKSB8PiAKICBnZ3Bsb3QoKSArCiAgZ2VvbV9oaXN0b2dyYW0oYWVzKHggPSBsZW5fZGVzYywgCiAgICAgICAgICAgICAgICAgICBmaWxsID0gYXMuZmFjdG9yKHllYXIpLCAKICAgICAgICAgICAgICAgICAgIGNvbG9yID0gYXMuZmFjdG9yKHllYXIpCiAgICAgICAgICAgICAgICAgICApLAogICAgICAgICAgICAgICBhbHBoYSA9IDAuMwogICAgICAgICAgICAgICApICsKICBmYWNldF93cmFwKH55ZWFyKQpgYGAKCgoKYGBge3J9Cm92X2R0IHw+IAogIGdncGxvdChhZXMoeD0gZGF0ZV9wdWJsaXNoZWQsIHkgPSBsZW5fdGl0bGUpKSArCiAgZ2VvbV9qaXR0ZXIoYWxwaGEgPSAwLjA1KSArCiAgZ2VvbV9zbW9vdGgoc3BhbiA9IDAuMSwgc2UgPSBGQUxTRSkgKwogIHRoZW1lX2xpZ2h0KCkgKwogIHNjYWxlX3lfbG9nMTAoKQoKb3ZfZHQgfD4gCiAgZ2dwbG90KGFlcyh4PSBkYXRlX3B1Ymxpc2hlZCwgeSA9IGxlbl9kZXNjKSkgKwogIGdlb21faml0dGVyKGFscGhhID0gMC4wNSkgKwogIGdlb21fc21vb3RoKHNwYW4gPSAwLjIsIHNlID0gRkFMU0UpICsKICB0aGVtZV9saWdodCgpICsKICBzY2FsZV95X2xvZzEwKCkKYGBgCgpgYGB7cn0KCmBgYAoKCiogV2hhdCBsaWNlbnNlIGlzIG1vc3QgdXNlZD8gSGFzIHRoZXJlIGJlZW4gYSBjaGFuZ2Ugb3ZlciB0aW1lPwoKYGBge3J9Cm92X2R0IHw+IAogIGdyb3VwX2J5KGxpY2Vuc2VfY2xlYW5lZCkgfD4gCiAgY291bnQoKSB8PiAKICBnZ3Bsb3QoYWVzKHggPSBmb3JjYXRzOjpmY3RfcmVvcmRlcihsaWNlbnNlX2NsZWFuZWQsIG4pLCB5ID0gbiwgZmlsbCA9IGxpY2Vuc2VfY2xlYW5lZCkpICsKICBnZW9tX2NvbCgpICsKICBjb29yZF9mbGlwKCkgKwogIHRoZW1lX21pbmltYWwoKSArCiAgZ3VpZGVzKGZpbGwgPSBGQUxTRSkgKwogIGxhYnMoeCA9ICIiLCB5ID0gIiIpCmBgYAoKYGBge3J9Cm92X2R0IHw+IAogIGdyb3VwX2J5KGR0KSB8PiAKICBjb3VudChsaWNlbnNlX2NsZWFuZWQpIHw+IAogIG11dGF0ZShsaWNlbnNlX2NsZWFuZWQgPSBmb3JjYXRzOjpmY3RfcmVvcmRlcihsaWNlbnNlX2NsZWFuZWQsIG4pKSB8PiAKICBnZ3Bsb3QoYWVzKHg9IGR0LCB5ID0gbiwgY29sb3IgPSBsaWNlbnNlX2NsZWFuZWQpKSArCiAgIyBnZW9tX2xpbmUoIGFscGhhID0gMC4zKSArCiAgZ2VvbV9qaXR0ZXIoYWxwaGEgPSAwLjMpICsKICBnZW9tX3Ntb290aChzcGFuID0gMC4zLCBzZSA9IEZBTFNFKSArCiAgdGhlbWVfbGlnaHQoKQpvdl9kdCB8PiAKICBncm91cF9ieShkdCkgfD4gCiAgY291bnQobGljZW5zZV9jbGVhbmVkKSB8PiAKICBtdXRhdGUobGljZW5zZV9jbGVhbmVkID0gZm9yY2F0czo6ZmN0X3Jlb3JkZXIobGljZW5zZV9jbGVhbmVkLCBuKSkgfD4gCiAgZ2dwbG90KGFlcyh4PSBkdCwgeSA9IG4sIGNvbG9yID0gbGljZW5zZV9jbGVhbmVkKSkgKwogICMgZ2VvbV9saW5lKCBhbHBoYSA9IDAuMykgKwogIGdlb21faml0dGVyKGFscGhhID0gMC4zKSArCiAgZ2VvbV9zbW9vdGgoc3BhbiA9IDAuOCwgc2UgPSBGQUxTRSkgKwogIHRoZW1lX2xpZ2h0KCkgKwogIHNjYWxlX3lfbG9nMTAoKQpgYGAKCiogRG8gcGFja2FnZXMgaGF2ZSBVUkxzIGZvciBidWcgcmVwb3J0cz8KCgpgYGB7cn0Kb3ZfZHQgfD4gCiAgZ3JvdXBfYnkoZHQpIHw+IAogIGNvdW50KHVybF9leGlzdCA9IGlzLm5hKHVybCkpIHw+ICAKICBnZ3Bsb3QoYWVzKHg9IGR0LCB5ID0gbiwgY29sb3IgPSB1cmxfZXhpc3QpKSArCiAgZ2VvbV9qaXR0ZXIoYWxwaGEgPSAwLjMpICsKICBnZW9tX3Ntb290aChzcGFuID0gMC4zLCBzZSA9IEZBTFNFKSArCiAgdGhlbWVfbGlnaHQoKQpvdl9kdCB8PiAKICBncm91cF9ieShkdCkgfD4gCiAgY291bnQodXJsX2V4aXN0ID0gaXMubmEoYnVnX3JlcG9ydHMpKSB8PiAgCiAgZ2dwbG90KGFlcyh4PSBkdCwgeSA9IG4sIGNvbG9yID0gdXJsX2V4aXN0KSkgKwogIGdlb21faml0dGVyKGFscGhhID0gMC4zKSArCiAgZ2VvbV9zbW9vdGgoc3BhbiA9IDAuMywgc2UgPSBGQUxTRSkgKwogIHRoZW1lX2xpZ2h0KCkKYGBgCgoqIFdoaWNoIHJlcG9zaXRvcmllcyBkbyBwYWNrYWdlcyB1c2U/IEdpdGh1Yi9CaXRidWNrZXQgZXRjLiBIb3cgZG8gdGhlc2UgdmFyeSBvdmVyIHRpbWU/CgpgYGB7cn0Kb3ZfZHQgfD4gCiAgZmlsdGVyKGRvbWFpbiAhPSAiIikgfD4gCiAgbXV0YXRlKGRvbWFpbiA9IGZvcmNhdHM6OmZjdF9sdW1wX21pbihkb21haW4sIDIwKSkgfD4gCiAgZ3JvdXBfYnkoZHQpIHw+IAogIGNvdW50KGRvbWFpbikgfD4gIAogIGdncGxvdChhZXMoeD0gZHQsIHkgPSBuLCBjb2xvciA9IGRvbWFpbikpICsKICBnZW9tX2ppdHRlcihhbHBoYSA9IDAuMykgKwogIGdlb21fc21vb3RoKHNwYW4gPSAwLjUsIHNlID0gRkFMU0UpICsKICB0aGVtZV9saWdodCgpCmBgYAoKKiBJcyB0aGVyZSBhbnkgdGVtcG9yYWwgcGF0dGVybnMgdG8gd2hlbiB2ZXJzaW9ucyBhcmUgc3VibWl0dGVkIHRvIENSQU4/CgpgYGB7cn0Kb3ZfZHQgfD4gCiAgZ3JvdXBfYnkoZHQpIHw+IAogIGNvdW50KCkgfD4gCiAgZ2dwbG90KGFlcyhkdCwgbikpICsKICBnZW9tX2xpbmUoKQoKb3ZfZHQgfD4gCiAgZmlsdGVyKCFpcy5uYShkdCkpIHw+IAogIGNvdW50KGR0KSB8PiAKICBhcnJhbmdlKGR0KSB8PiAKICB0aW1ldGs6OnBhZF9ieV90aW1lKC5ieSA9ICJtb250aCIsIC5wYWRfdmFsdWUgPSAwKSAtPiB4ZGF0CnRpbWV0azo6cGxvdF9zZWFzb25hbF9kaWFnbm9zdGljcyh4ZGF0LCBkdCwgbikKYGBgCgo=